/*
 * mainPrompt.cpp
 *
 *  Created on: 16/11/2011
 *      Author: joagf
 */

#include <iostream>
#include <pthread.h>
#include <cstdlib>
#include <sys/time.h>
#include "semaphore.h"
#include <vector>


#define RIGHT 0
#define LEFT 1
#define MAXM 50
#define MAX_BABU_BRIDGE 3

/*Estados da ponte*/
#define ENABLE 1		/*Ponte livre*/
#define RIGHTDISABLE 3	/*Babuinos vindos da esquerda estão atualmente atravessando a ponte*/
#define LEFTDISABLE 2	/*Babuinos vindos da direita estão atualmente atravessando a ponte*/


Semaphore right; 		/*Semaforo de controle dos Babuinos vindos da direita*/
Semaphore left;			/*Semaforo de controle dos Babuinos vindos da esquerda*/
Semaphore brg;			/*Exclusão mutua para acessar a condição da ponte*/

/*Struct para comunicação das threads*/
typedef struct
{
	int origin; 		/*Origem do Babuino RIGHT ou LEFT*/
	int id;				/*Número de indentificação do Babuino*/
}Babu;

using std::vector;


vector <int> BabuQueue;	/*Fila de Babuinos a espera para acessar a ponte*/
vector <int> inBrigde;	/*Vetor de Babuinos que estão atravessando a ponte*/

/*Exclusão mutua */
Semaphore sBabuQueue;
Semaphore sInBrigde;

/*Condição atual da ponte*/
int brigde = ENABLE;
int totalBabuEsq = 0;
double base_time = 0;
double ending_time = 0;
double sum_wait = 0; //soma o tempo em fila
double sum_wait_q = 0; //qtde de somas no sum_wait
double time_brigde = 0;
double free_brigde = 0;
Semaphore counting;


/*Thread de controle que permiti o acesso ou não à ponte*/

void *control(void *arg)
{
	int r,l,turn,bri,cnt=0;
	unsigned int i,j;
	r = 0;
	l = 0;
	while(true)
	{
		down(&sBabuQueue);
		if (BabuQueue.size() > 0)/*checa se existe babuinos na fila querendo entrar na ponte*/
		{
			i = 0;
			while(true)
			{
				if (i < BabuQueue.size())
				{
					turn = BabuQueue[i];	/*Pega a origem do babuino I*/
					down(&brg);
					bri = brigde;			/*Captura a condição atual da ponte*/
					up(&brg);
					if (turn == RIGHT && (bri == ENABLE || bri == LEFTDISABLE ) )	/*Se o babuino vem da direita e a ponte esta disponivel*/
					{

						r++;
						if (r <= MAX_BABU_BRIDGE) 	/*Checa se MAX_BABU_BRIDGE direitos atravessaram a ponte sem permitir um esquerdo atravessar,caso não o controle libera um babuino direito*/
						{
							up(&right);
							down(&brg);
							brigde = LEFTDISABLE;  /*Como existe babuinos direitos na ponte, esquerdos não podem atravessar*/
							up(&brg);
							BabuQueue.erase(BabuQueue.begin()+i); 	/*Após ter liberado o babuino remove ele da fila*/
							cnt++;
							break;
						}
						else /*Se MAX_BABU_BRIDGE babuinos seguidos direitos atravesseram a ponte, procura um babuino esquerdo na fila para liberar*/
						{
							for (j = 0; j < BabuQueue.size(); j++ )
							{
								if (BabuQueue[j] == LEFT);
								{
									i = j;
									break;
								}
							}
							if (BabuQueue[i] == LEFT)
							{
								continue;
							}
							/*Caso não encontre algum babuino esquerdo retoma os direitos para 0*/
							r = 0;

						}
					}
					if (turn == LEFT && (bri == ENABLE || bri == RIGHTDISABLE ))
					{
						l++;
						if (l <= MAX_BABU_BRIDGE)
						{

							up(&left);
							down(&brg);
							brigde = RIGHTDISABLE;
							up(&brg);
							BabuQueue.erase(BabuQueue.begin()+i);
							cnt++;
							break;
						}
						else
						{
							for (j = 0; j < BabuQueue.size(); j++ )
							{
								if (BabuQueue[j] == RIGHT);
								{
									i = j;
									break;
								}
							}
							if (BabuQueue[i] == RIGHT)
							{
								continue;
							}
							l = 0;
						}
					}
					i++;
				}
				else
				{

					i = 0;

				}
			}


		}
		up(&sBabuQueue);
		usleep(100000);
		std::cout.flush();
		if (cnt == MAXM)
		{
			struct timeval oi;
			gettimeofday(&oi, NULL);
			ending_time = oi.tv_sec; //+ tim.tv_usec/1000000.0;
			time_brigde = ending_time-base_time ;
			std::cout << "end " << totalBabuEsq << " " << MAXM<<std::endl;
			std::cout.flush();
			std::cout << sum_wait/sum_wait_q << " " << (time_brigde- free_brigde)<< " " <<time_brigde<<std::endl;
			std::cout.flush();
			return NULL;
		}
	}
	return  NULL;
}


void *monkey(void *arg)
{
	Babu *b = (Babu*) arg;
	struct timeval tim;
	double t1, t2;


	if (b->origin == RIGHT)
	{
		sleep(1);
		down(&sBabuQueue);
		BabuQueue.push_back(RIGHT); 							/*Coloca um babuino na fila de espera*/
		up(&sBabuQueue);
		std::cout<<"Chegou "<<b->id<<" Direito\n";
		std::cout.flush();

		gettimeofday(&tim, NULL);
		t1 = tim.tv_sec + tim.tv_usec/1000000.0;

		down(&right);//esperando acesso

		gettimeofday(&tim, NULL);
		t2 = tim.tv_sec + tim.tv_usec/1000000.0;

		down(&counting);
		sum_wait += t2 - t1+5;
		sum_wait_q++;
		up(&counting);

		down(&sInBrigde);
		inBrigde.push_back(RIGHT);								/*Coloca um babuino na ponte*/
		up(&sInBrigde);
		std::cout<<"Atravessando "<<b->id<<" Direito\n";
		std::cout.flush();
		sleep(4);//tempo de atravessia da ponte
		std::cout<<"Atravessou "<<b->id<<" Direito\n";

		std::cout.flush();

		down(&sInBrigde);
		inBrigde.erase(inBrigde.begin());						/*Babuino atravessou remova-o da ponte*/
		if (inBrigde.size() == 0)
		{
			brigde = ENABLE;									/*Se não há mais babuinos na ponte, coloque ponte disponivel*/
			//std::cout<<"Ponte disponivel para ambos\n";
		}
		up(&sInBrigde);

	}
	else
	{
		sleep(1);
		down(&sBabuQueue);
		BabuQueue.push_back(LEFT);
		up(&sBabuQueue);
		std::cout<<"Chegou "<<b->id<<" Esquerdo\n";
		std::cout.flush();

		gettimeofday(&tim, NULL);
		t1 = tim.tv_sec + tim.tv_usec/1000000.0;

		down(&left);//esperando acesso

		gettimeofday(&tim, NULL);
		t2 = tim.tv_sec + tim.tv_usec/1000000.0;

		down(&counting);
		sum_wait += t2 - t1+5;//tempo em espera
		sum_wait_q++;
		up(&counting);

		down(&sInBrigde);
		inBrigde.push_back(LEFT);
		up(&sInBrigde);
		std::cout<<"Atravessando "<<b->id<<" Esquerdo\n";
		std::cout.flush();
		sleep(4);//tempo de atravessia da ponte
		std::cout<<"Atravessou "<<b->id<<" Esquerdo\n";
		std::cout.flush();
		down(&sInBrigde);
		inBrigde.erase(inBrigde.begin());
		if (inBrigde.size() == 0)
		{
			brigde = ENABLE;
		}
		up(&sInBrigde);

	}
	return NULL;
}


/*Gerar os babubinos para ponte*/
void* generate(void *arg)
{
	int timeWait,num,i,seed = static_cast <int>(time(NULL));
	srand(seed);

	pthread_t line[MAXM];
	Babu b[MAXM];
	for (i = 1; i <= MAXM; i++)
	{

		num = rand() % RAND_MAX + 1;
		if (num % 2)
		{
			b[i-1].id = i;
			b[i-1].origin = RIGHT;
			std::cout<<"Chegando "<<b[i-1].id<<" Direito\n";
			pthread_create(&line[i-1],NULL,monkey,(void*)&b[i-1]);
		}
		else
		{
			b[i-1].id = i;
			b[i-1].origin = LEFT;
			totalBabuEsq++;
			std::cout<<"Chegando "<<b[i-1].id<<" Esquerdo\n";
			pthread_create(&line[i-1],NULL,monkey,(void*)&b[i-1]);
		}
		std::cout.flush();
		timeWait = rand() % 8 + 1;
		sleep(timeWait);
	}
	return NULL;
}

void* calcs(void *arg) {
	struct timeval tim;
	double t1, t2;
	t1 = t2 = 0;
	while (true) {
		if (brigde == ENABLE) {
			gettimeofday(&tim, NULL);
			t1 = tim.tv_sec + tim.tv_usec/1000000.0;
			while (brigde == ENABLE);//espera
			gettimeofday(&tim, NULL);
			t2 = tim.tv_sec + tim.tv_usec/1000000.0;
			free_brigde += t2-t1;
			t1 = t2 = 0;
		}
	}

}

/*int main() {
	struct timeval tim;

	pthread_t line1, line2, line3;
	init_semaphore(&right,0);
	init_semaphore(&left,0);
	init_semaphore(&brg,1);
	init_semaphore(&sBabuQueue,1);
	init_semaphore(&sInBrigde,1);
	init_semaphore(&counting, 1);

	gettimeofday(&tim, NULL);
	base_time = tim.tv_sec + tim.tv_usec/1000000.0;

	pthread_create(&line2,NULL,control,NULL);
	pthread_create(&line1, NULL, generate, NULL);
	pthread_create(&line3, NULL, calcs, NULL);


	pthread_exit(NULL);
	return 0;
}*/





